package com.yixing.tech.entity.entity.attr;

import com.alibaba.fastjson.JSON;
import com.yixing.tech.common.base.BusinessException;
import com.yixing.tech.common.enums.ErrCode;
import com.yixing.tech.common.enums.YesOrNo;
import com.yixing.tech.entity.entity.CustomActionParam;
import com.yixing.tech.entity.service.impl.ObjProcessor;
import com.yixing.tech.entity.po.EntityData;
import com.yixing.tech.entity.service.EntityBaseService;
import com.yixing.tech.common.utils.CommonUtil;
import com.yixing.tech.common.utils.DataUtil;
import com.yixing.tech.common.vo.s5.EntitySchema;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;

/**
 * @author yixing tech
 * at 2021/10/9
 * describe：
 */
@Component
public final class InsertEvent extends AttrType {
    @Autowired
    private EntityBaseService entityBaseService;
    @Resource
    private ObjProcessor objProcessor;
    @Resource
    private MongoTemplate mongoTemplate;
    private static InsertEvent self;

    @PostConstruct
    private void init() {
        self = this;
    }

    public InsertEvent() {
        super("关联插入", "insertEvent");
    }

    /**
     * 行为
     *
     * @param param
     */
    @Override
    public void exeAction(CustomActionParam param, List<EntitySchema.AttrVO> attrs) throws BusinessException {
        super.exeAction(param, attrs);
        String templateId = param.getObjCode();
        String entityId = param.getEntityId();
        String actionCode = param.getMethod();
        EntitySchema.AttrVO attr = self.objProcessor.getAttr(actionCode, attrs);
        Object ref = attr.getReference();
        if (CommonUtil.stringIsBlank(ref)) throw new BusinessException(ErrCode.MISS_CONFIG);
        EntitySchema.AttrVO.InsertEventReference reference;
        if(ref instanceof Map){
            reference = DataUtil.o2o(ref, EntitySchema.AttrVO.InsertEventReference.class);
        }else{
            reference = JSON.parseObject(ref.toString(), EntitySchema.AttrVO.InsertEventReference.class);
        }
        String targetTemplateId = reference.getMeteObj();
        Map<String, String> config = reference.getInsertConfig();
        if (ObjectUtils.isEmpty(config)) throw new BusinessException(ErrCode.MISS_CONFIG);
        String insertList = reference.getInsertList();
        Document document = self.entityBaseService.getEntity(templateId, entityId, null, false);
        if (document.getBoolean(actionCode, false)) return;
        EntitySchema targetObj = self.entityBaseService.queryByTableId(targetTemplateId);
        List<EntitySchema.AttrVO> targetAttrs = targetObj.getAttrs();
        Map<String, Object> tempMap = new HashMap<>(5);
        /**
         * 批量插入
         */
        if (!CommonUtil.stringIsBlank(insertList)) {
            List<Document> insertObjs = document.getList(insertList, Document.class);
            if (!CommonUtil.listIsBlank(insertObjs)) {
                for (Document doc : insertObjs) {
                    document.put(insertList, doc);
                    actionInsertObj(targetTemplateId, targetAttrs, document, tempMap, config, param, attrs, reference.getAggregationConfig());
                }
            }
        } else {
            actionInsertObj(targetTemplateId, targetAttrs, document, tempMap, config, param, attrs, reference.getAggregationConfig());
        }
        Update update = new Update();
        update.set(actionCode, true);
        self.mongoTemplate.upsert(Query.query(Criteria.where("_id").is(new ObjectId(entityId))), update, "entity_" + templateId);
    }

    private void actionInsertObj(String targetTemplateId, List<EntitySchema.AttrVO> targetAttrs, Document document, Map<String, Object> tempMap, Map<String, String> config, CustomActionParam param, List<EntitySchema.AttrVO> attrs, EntitySchema.AttrVO.InsertEventReference.AggregationConfig aggregationConfig) throws BusinessException {
        EntityData values = new EntityData();
        for (String key : config.keySet()) {
            String valueConfig = config.get(key);
            if (valueConfig.startsWith("$")) {
                String[] codes = valueConfig.split("\\.");
                AttrType attrType = self.objProcessor.getAttrType(key, targetAttrs);
                Object value = self.objProcessor.getRefValue(attrs, codes, document, tempMap, attrType);
                if (attrType instanceof TimeText && value == null) value = new Date();
                values.put(key, value);
            } else {
                values.put(key, valueConfig);
            }
        }
        if (aggregationConfig == null||ObjectUtils.isEmpty(aggregationConfig.getGroupKeys())) {
            self.objProcessor.fillCreateUser(values, param.getUserVO());
            self.entityBaseService.insetEntity(targetTemplateId, values, false);
        } else {
            List<String> groupKeys = aggregationConfig.getGroupKeys();
            List<String> accrualKeys = aggregationConfig.getAccrualKeys();
            if (ObjectUtils.isEmpty(groupKeys) || ObjectUtils.isEmpty(accrualKeys))
                throw new BusinessException(ErrCode.MISS_CONFIG);
            Criteria criteria = Criteria.where("isDelete").ne(YesOrNo.YES.ordinal());
            List<Criteria> criteriaList = new ArrayList<>();
            for (String key : groupKeys) {
                criteriaList.add(Criteria.where(key).is(values.get(key)));
            }
            criteria.andOperator(criteriaList);
            Document target = self.mongoTemplate.findOne(Query.query(criteria), Document.class, "entity_" + targetTemplateId);
            if (target == null) {
                self.objProcessor.fillCreateUser(values, param.getUserVO());
                self.entityBaseService.insetEntity(targetTemplateId, values, false);
            } else {
                String id = target.getString("id");
                EntityData entity = new EntityData();
                entity.put("id", id);
                for (String key : accrualKeys) {
                    double old = Double.parseDouble(target.get(key).toString());
                    double n = Double.parseDouble(values.get(key).toString());
                    entity.put(key, BigDecimal.valueOf(old).add(BigDecimal.valueOf(n)));
                }
                self.entityBaseService.updateEntityAsync(targetTemplateId, entity);
            }
        }

    }

}
